#pragma once

#include <iostream>
#include "defaults.h"
#include "uWS.h"
#include <iostream>
#include <chrono>
#include <cmath>
#include <thread>
#include <fstream>
#include <vector>
#include <set>
#include <unordered_set>
#include <unordered_map>
#include <map>
#include <atomic>
#include "peer_connection_wsclient.h"
#include "rtc_base/json.h"
#include "JanusHandle.h"
#include "JanusTransaction.h"
#include <list>
#include "rtc_base/checks.h"
#include "rtc_base/json.h"
#include "rtc_base/logging.h"

#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/video_codecs/builtin_video_decoder_factory.h"
#include "api/video_codecs/builtin_video_encoder_factory.h"
#include "api/test/fakeconstraints.h"
#include "defaults.h"
#include "media/engine/webrtcvideocapturerfactory.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "modules/video_capture/video_capture_factory.h"
#include "peer_connection.h"

#include "rtc_base/arraysize.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/stringutils.h"
#include "rtc_base/win32socketserver.h"
#include "rtc_base/win32socketinit.h"

#include "third_party/libyuv/include/libyuv/convert_argb.h"

#if defined(WEBRTC_WIN)
#include "rtc_base/win32.h"
#endif

using namespace std;
using namespace rtc;

struct REMOTE_SDP_INFO {
    long long int handleId;
    std::string jsep_str;
};

class ImplPeerConnectionWsClientObserver :
    public sigslot::has_slots<>,
    public PeerConnectionWsClientObserver {
public:
    ImplPeerConnectionWsClientObserver() {

    };

};

class VideoRoomClient :
    public PeerConnectionWsClientObserver,
    public PeerConnectionCallback,
    public sigslot::has_slots<> {
public:
    VideoRoomClient();
    void SendBitrateConstraint(long long int handleId);
    std::unique_ptr<cricket::VideoCapturer> OpenVideoCaptureDevice();
    void AddTracks(long long int handleId);
    bool CreatePeerConnection(long long int handleId, bool dtls);
    bool InitializePeerConnection(long long int handleId, bool bPublisher);

    void DeletePeerConnection(long long int handleId);
    void ConectToServer(std::string ip, int port);
    void UIThreadCallback(int msg_id, void* data);
    void SendOffer(long long int handleId, std::string sdp_type, std::string sdp_desc);
    void SendAnswer(long long int handleId, std::string sdp_type, std::string sdp_desc);
    void trickleCandidate(long long int handleId, const webrtc::IceCandidateInterface* candidate);
    void trickleCandidateComplete(long long int handleId);

protected:
    virtual void PCSendSDP(long long int handleId, std::string sdpType, std::string sdp);
    virtual void PCQueueUIThreadCallback(int msg_id, void* data);
    virtual void PCTrickleCandidate(long long int handleId, const webrtc::IceCandidateInterface* candidate);

    virtual void PCTrickleCandidateComplete(long long int handleId);

    virtual void OnSignedIn();;
    virtual void OnDisconnected();
    virtual void OnPeerConnected(int id, const std::string& name);
    virtual void OnMessageFromJanus(int peer_id, const std::string& message);
    virtual void OnMessageSent(int err);
    virtual void OnServerConnectionFailure();
    virtual void OnJanusConnected();
    virtual void OnJanusDisconnected();
    virtual void OnSendKeepAliveToJanus();

public:

    void KeepAlive();
    void JoinRoom(std::string pluginName, long long int handleId, long long int feedId);
    void CreateHandle(std::string pluginName, long long int feedId, std::string display);
    void CreateSession();;

    PeerConnectionWsClient* mWsClient;
    long long int m_SessionId = 0;
    std::map<std::string, std::shared_ptr<JanusTransaction>> m_transactionMap;
    std::map<long long int, std::shared_ptr<JanusHandle>> m_handleMap;
    std::map<long long int, rtc::scoped_refptr<PeerConnection>> m_peer_connection_map;
    rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> peer_connection_factory_;
    std::unique_ptr<Thread> m_signaling_thread;
    std::unique_ptr<Thread> m_worker_thread;
    std::unique_ptr<Thread> m_network_thread;

};